<?php

namespace Dongasai\WkProtoPhp\Packets;

use Dongasai\WkProtoPhp\Enums\FrameType;
use Dongasai\WkProtoPhp\Framer;
use Dongasai\WkProtoPhp\Encoder;
use Dongasai\WkProtoPhp\Decoder;
use Dongasai\WkProtoPhp\Enums\ReasonCode;

/**
 * CONNACK连接确认包
 */
class ConnackPacket extends Framer
{
    private int $serverVersion = 0;
    private string $serverKey = '';
    private string $salt = '';
    private int $timeDiff = 0;
    private int $reasonCode = ReasonCode::SUCCESS->value;
    private int $nodeId = 0;

    public function __construct()
    {
        parent::__construct(FrameType::CONNACK->value);
    }

    public function getFrameType(): int
    {
        return FrameType::CONNACK->value;
    }

    public function __toString(): string
    {
        return sprintf(
            'CONNACK TimeDiff: %d ReasonCode:%s ServerVersion:%d NodeId:%d',
            $this->timeDiff,
            ReasonCode::toString($this->reasonCode),
            $this->serverVersion,
            $this->nodeId
        );
    }

    // Getters and Setters
    public function getServerVersion(): int
    {
        return $this->serverVersion;
    }

    public function setServerVersion(int $serverVersion): void
    {
        $this->serverVersion = $serverVersion;
    }

    public function getServerKey(): string
    {
        return $this->serverKey;
    }

    public function setServerKey(string $serverKey): void
    {
        $this->serverKey = $serverKey;
    }

    public function getSalt(): string
    {
        return $this->salt;
    }

    public function setSalt(string $salt): void
    {
        $this->salt = $salt;
    }

    public function getTimeDiff(): int
    {
        return $this->timeDiff;
    }

    public function setTimeDiff(int $timeDiff): void
    {
        $this->timeDiff = $timeDiff;
    }

    public function getReasonCode(): int
    {
        return $this->reasonCode;
    }

    public function setReasonCode(int $reasonCode): void
    {
        $this->reasonCode = $reasonCode;
    }

    public function getNodeId(): int
    {
        return $this->nodeId;
    }

    public function setNodeId(int $nodeId): void
    {
        $this->nodeId = $nodeId;
    }

    /**
     * 解码CONNACK包
     */
    public static function decode(Framer $framer, string $data, int $version): self
    {
        $decoder = new Decoder($data);
        $packet = new self();
        
        // 复制framer属性
        $packet->setRemainingLength($framer->getRemainingLength());
        $packet->setNoPersist($framer->getNoPersist());
        $packet->setRedDot($framer->getRedDot());
        $packet->setSyncOnce($framer->getSyncOnce());
        $packet->setDUP($framer->getDUP());
        $packet->setFrameSize($framer->getFrameSize());
        $packet->setHasServerVersion($framer->getHasServerVersion());

        try {
            if ($framer->getHasServerVersion()) {
                $packet->serverVersion = $decoder->uint8();
            }

            $packet->timeDiff = $decoder->int64();
            $packet->reasonCode = $decoder->uint8();
            $packet->serverKey = $decoder->string();
            $packet->salt = $decoder->string();

            if ($version >= 4) {
                $packet->nodeId = $decoder->uint64();
            }
        } catch (\Exception $e) {
            throw new \RuntimeException('解码CONNACK包失败: ' . $e->getMessage(), 0, $e);
        }

        return $packet;
    }

    /**
     * 编码CONNACK包
     */
    public function encode(Encoder $encoder, int $version): void
    {
        if ($this->getHasServerVersion()) {
            $encoder->writeUint8($this->serverVersion);
        }

        $encoder->writeInt64($this->timeDiff);
        $encoder->writeUint8($this->reasonCode & 0xFF);
        $encoder->writeString($this->serverKey);
        $encoder->writeString($this->salt);

        if ($version >= 4) {
            $encoder->writeUint64($this->nodeId);
        }
    }

    /**
     * 计算CONNACK包大小
     */
    public function size(int $version): int
    {
        $size = 0;

        if ($this->getHasServerVersion()) {
            $size += 1; // VersionByteSize
        }

        $size += 8; // TimeDiffByteSize
        $size += 1; // ReasonCodeByteSize
        $size += 2 + strlen($this->serverKey); // StringFixLenByteSize + serverKey length
        $size += 2 + strlen($this->salt); // StringFixLenByteSize + salt length

        if ($version >= 4) {
            $size += 8; // NodeIdByteSize
        }

        return $size;
    }
}